`include "mycpu.h"
module csr(
    input clk,
    input reset,
    input csr_re,
    input [13:0] csr_num,
    output [31:0] csr_rvalue,
    input csr_we,
    input [31:0] csr_wmask,
    input [31:0] csr_wvalue,
    //from WB
    input [31:0] wb_pc,
    input wb_ex,
    input [5:0] wb_ecode,
    input [8:0] wb_esubcode,
    input ertn_flush,
    //to ID
    output has_int,
    //to pre-IF
    output [31:0] ex_entry,
    //hardware interupt
    input [7:0] hw_int_in,
    //ipi interupt
    input ipi_int_in
);

reg [1:0] csr_crmd_plv;
reg [1:0] csr_prmd_pplv;
reg csr_crmd_ie;
reg csr_prmd_pie;
reg [12:0] csr_ecfg_lie;
reg [12:0] csr_estat_is;
reg [5:0] csr_estat_ecode;
reg [8:0] csr_estat_esubcode;
reg [31:0] csr_era_pc;
reg [25:0] csr_eentry_va;
reg [31:0] csr_save0_data;
reg [31:0] csr_save1_data;
reg [31:0] csr_save2_data;
reg [31:0] csr_save3_data;
wire [31:0] csr_crmd_rvalue;
wire [31:0] csr_prmd_rvalue;
wire [31:0] csr_ecfg_rvalue;
wire [31:0] csr_estat_rvalue;
wire [31:0] csr_era_rvalue;
wire [31:0] csr_eentry_rvalue;
wire [31:0] csr_save0_rvalue;
wire [31:0] csr_save1_rvalue;
wire [31:0] csr_save2_rvalue;
wire [31:0] csr_save3_rvalue;
//crmd
    assign csr_crmd_rvalue = {28'b0, 1'b1, csr_crmd_ie, csr_crmd_plv};
    //PLV
    always @(posedge clk) begin
        if(reset)
            csr_crmd_plv <= 2'b0;
        else if(wb_ex)
            csr_crmd_plv <= 2'b0;
        else if(ertn_flush)
            csr_crmd_plv <= csr_prmd_pplv;
        else if(csr_we && csr_num == `CSR_CRMD)
            csr_crmd_plv <= csr_wmask[`CSR_CRMD_PLV] & csr_wvalue[`CSR_CRMD_PLV]
                        |  ~csr_wmask[`CSR_CRMD_PLV] & csr_crmd_plv;
    end
    //IE
    always @(posedge clk) begin
        if(reset)
            csr_crmd_ie <= 1'b0;
        else if(wb_ex)
            csr_crmd_ie <= 1'b0;
        else if(ertn_flush)
            csr_crmd_ie <= csr_prmd_pie;
        else if(csr_we && csr_num == `CSR_CRMD)
            csr_crmd_ie <= csr_wmask[`CSR_CRMD_IE] & csr_wvalue[`CSR_CRMD_IE]
                        | ~csr_wmask[`CSR_CRMD_IE] & csr_crmd_ie;
    end
//prmd
    assign csr_prmd_rvalue = {28'b0, 1'b0, csr_prmd_pie, csr_prmd_pplv};
    //PLV
    always @(posedge clk) begin
        if(wb_ex) begin
            csr_prmd_pplv <= csr_crmd_plv;
            csr_prmd_pie <= csr_crmd_ie;
        end
        else if(csr_we && csr_num == `CSR_PRMD) begin
            csr_prmd_pie <= csr_wmask[`CSR_PRMD_PIE] & csr_wvalue[`CSR_PRMD_PIE]
                        |  ~csr_wmask[`CSR_PRMD_PIE] & csr_prmd_pie;
            csr_prmd_pplv <= csr_wmask[`CSR_PRMD_PPLV] & csr_wvalue[`CSR_PRMD_PPLV]
                        |  ~csr_wmask[`CSR_PRMD_PPLV] & csr_prmd_pplv;
        end
    end
//ecfg
    assign csr_ecfg_rvalue = {19'b0, csr_ecfg_lie};
    //LIE
    always @(posedge clk) begin
        if(reset)
            csr_ecfg_lie <= 13'b0;
        else if(csr_we & csr_num == `CSR_ECFG) 
            csr_ecfg_lie <= csr_wmask[`CSR_ECFG_LIE] & csr_wvalue[`CSR_ECFG_LIE]
                        |  ~csr_wmask[`CSR_ECFG_LIE] & csr_ecfg_lie;
    end
//estat
    assign csr_estat_rvalue = {1'b0, csr_estat_esubcode, csr_estat_ecode, 3'b0, 
                                                                 csr_estat_is};
    //IS
    always @(posedge clk) begin
        if(reset)
            csr_estat_is[1:0] <= 2'b0;
        else if(csr_we && csr_num == `CSR_ESTAT)
            csr_estat_is[1:0] <= csr_wmask[`CSR_ESTAT_IS10] & csr_wvalue[`CSR_ESTAT_IS10]
                            |   ~csr_wmask[`CSR_ESTAT_IS10] & csr_estat_is[`CSR_ESTAT_IS10];
        
        csr_estat_is[9:2] <= hw_int_in[7:0];
        csr_estat_is[10] <= 1'b0;
        //tmp
        csr_estat_is[11] <= 1'b0;
        
        csr_estat_is[12] <= ipi_int_in;
    end                            
    //ECODE, ESUBCODE
    always @(posedge clk) begin
        if(wb_ex) begin
            csr_estat_ecode <= wb_ecode;
            csr_estat_esubcode <= wb_esubcode;
        end
    end        
//era
    assign csr_era_rvalue = csr_era_pc;
    //pc
    always @(posedge clk) begin
        if(wb_ex)
            csr_era_pc <= wb_pc;
        else if (csr_we && csr_num==`CSR_ERA)
            csr_era_pc <= csr_wmask[`CSR_ERA_PC] & csr_wvalue[`CSR_ERA_PC]
                       | ~csr_wmask[`CSR_ERA_PC] & csr_era_pc;
    end                     
//eentry
    assign csr_eentry_rvalue = {csr_eentry_va, 6'b0};
    //VA
    always @(posedge clk) begin
        if(csr_we && csr_num == `CSR_EENTRY)
            csr_eentry_va <= csr_wmask[`CSR_EENTRY_VA] & csr_wvalue[`CSR_EENTRY_VA]
                          | ~csr_wmask[`CSR_EENTRY_VA] & csr_eentry_va;
    end
//save
    assign csr_save0_rvalue = csr_save0_data;
    assign csr_save1_rvalue = csr_save1_data;
    assign csr_save2_rvalue = csr_save2_data;
    assign csr_save3_rvalue = csr_save3_data;
    //DATA
    always @(posedge clk) begin
        if (csr_we && csr_num==`CSR_SAVE0)
            csr_save0_data <= csr_wmask[`CSR_SAVE_DATA] & csr_wvalue[`CSR_SAVE_DATA]
                           | ~csr_wmask[`CSR_SAVE_DATA] & csr_save0_data;
        if (csr_we && csr_num==`CSR_SAVE1)
            csr_save1_data <= csr_wmask[`CSR_SAVE_DATA] & csr_wvalue[`CSR_SAVE_DATA]
                           | ~csr_wmask[`CSR_SAVE_DATA] & csr_save1_data;
        if (csr_we && csr_num==`CSR_SAVE2)
            csr_save2_data <= csr_wmask[`CSR_SAVE_DATA] & csr_wvalue[`CSR_SAVE_DATA]
                           | ~csr_wmask[`CSR_SAVE_DATA] & csr_save2_data;
        if (csr_we && csr_num==`CSR_SAVE3)
            csr_save3_data <= csr_wmask[`CSR_SAVE_DATA] & csr_wvalue[`CSR_SAVE_DATA]
                           | ~csr_wmask[`CSR_SAVE_DATA] & csr_save3_data;
    end
//read value
    assign csr_rvalue = {32{csr_num == `CSR_CRMD}} & csr_crmd_rvalue
                    |   {32{csr_num == `CSR_PRMD}} & csr_prmd_rvalue
                    |   {32{csr_num == `CSR_ECFG}} & csr_ecfg_rvalue
                    |   {32{csr_num == `CSR_ESTAT}} & csr_estat_rvalue
                    |   {32{csr_num == `CSR_ERA}} & csr_era_rvalue
                    |   {32{csr_num == `CSR_EENTRY}} & csr_eentry_rvalue
                    |   {32{csr_num == `CSR_SAVE0}} & csr_save0_rvalue
                    |   {32{csr_num == `CSR_SAVE1}} & csr_save1_rvalue
                    |   {32{csr_num == `CSR_SAVE2}} & csr_save2_rvalue
                    |   {32{csr_num == `CSR_SAVE3}} & csr_save3_rvalue;
//ex entry
    assign ex_entry = csr_eentry_rvalue;
endmodule